# coding: utf-8
# Project：PyCharmMiscProject
# File：Python结课实验4.py
# Author：liuchuanzhi
# Date ：2025/4/30 19:39
# IDE：PyCharm
import socket
import threading
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os
from datetime import datetime
import time


class VotingSystem:
    # 保持原有实现不变（省略重复代码，实际使用时需保留完整类定义）
    def __init__(self, vote_mode, mode_value):
        self.candidates = {}
        self.valid_voters = set()
        self.invalid_votes = []
        self.vote_mode = vote_mode
        self.mode_value = mode_value
        self.lock = threading.Lock()

    def add_candidate(self, name):
        with self.lock:
            cid = len(self.candidates) + 1
            self.candidates[cid] = [name, 0]
            return cid

    def validate_vote(self, user_id, candidate_ids):
        with self.lock:
            if user_id in self.valid_voters:
                return False, "重复投票（已成功投过有效票）"
            num_votes = len(candidate_ids)
            if self.vote_mode == 'max':
                if num_votes < 1:
                    return False, "投票数量不足（至少投1个）"
                if num_votes > self.mode_value:
                    return False, f"超过最大限制（最多{self.mode_value}个）"
            elif self.vote_mode == 'fixed':
                if num_votes != self.mode_value:
                    return False, f"数量不符（必须投{self.mode_value}个）"
            if len(set(candidate_ids)) != num_votes:
                return False, "包含重复候选人"
            invalid_cids = [cid for cid in candidate_ids if cid not in self.candidates]
            if invalid_cids:
                return False, f"无效候选人ID：{invalid_cids}（有效：{list(self.candidates.keys())}）"
            return True, "有效票"

    def process_vote(self, user_id, candidate_ids):
        is_valid, reason = self.validate_vote(user_id, candidate_ids)
        with self.lock:
            if not is_valid:
                self.invalid_votes.append((user_id, reason))
                return f"无效票：{reason}"
            self.valid_voters.add(user_id)
            for cid in candidate_ids:
                self.candidates[cid][1] += 1
            return f"有效票：成功为{len(candidate_ids)}个候选人投票"

    def get_summary(self):
        summary = [f"=== 投票统计报告（{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}）==="]
        summary.append("\n=== 候选人得票结果 ===")
        for cid, (name, votes) in sorted(self.candidates.items(), key=lambda x: x[1][1], reverse=True):
            summary.append(f"候选人{cid}【{name}】: {votes}票")
        summary.append("\n=== 投票统计汇总 ===")
        summary.append(f"有效投票用户数: {len(self.valid_voters)}")
        summary.append(f"无效投票数: {len(self.invalid_votes)}")
        if self.invalid_votes:
            summary.append("无效票详情:")
            for idx, (user_id, reason) in enumerate(self.invalid_votes, 1):
                summary.append(f"{idx}. 用户{user_id}: {reason}")
        return "\n".join(summary)

    def save_to_file(self, file_path):
        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(self.get_summary())
            return f"结果已成功保存到：{file_path}"
        except Exception as e:
            return f"保存失败：{str(e)}"


def handle_client(client_socket, voting_system, key, iv):
    try:
        client_socket.sendall(key + iv)
        while True:
            encrypted_data = client_socket.recv(1024)
            if not encrypted_data:
                break
            try:
                cipher = AES.new(key, AES.MODE_CBC, iv)
                decrypted_data = unpad(cipher.decrypt(encrypted_data[AES.block_size:]), AES.block_size)
                raw_data = decrypted_data.decode('utf-8')
            except Exception:
                response = "无效票：数据格式错误"
            else:
                try:
                    user_id, cids_str = raw_data.split('|')
                    candidate_ids = list(map(int, cids_str.split(',')))
                except:
                    response = "无效票：数据格式错误（正确格式：用户ID|候选人ID1,ID2）"
                else:
                    response = voting_system.process_vote(user_id, candidate_ids)
            try:
                cipher = AES.new(key, AES.MODE_CBC, iv)
                encrypted_response = cipher.encrypt(pad(response.encode('utf-8'), AES.block_size))
                client_socket.sendall(iv + encrypted_response)
            except Exception:
                pass
    except Exception as e:
        print(f"客户端处理异常: {str(e)}")
    finally:
        client_socket.close()  # 确保关闭客户端连接
        print("客户端连接关闭")


def main():
    # 初始化投票规则（保持不变）
    print("=== 投票规则设置 ===")
    while True:
        mode = input("请选择投票模式（输入'最大'或'固定'）: ").strip()
        if mode not in ['最大', '固定']:
            print("输入错误，请重新输入！")
            continue
        while True:
            try:
                value = int(input(f"请输入{mode}投票数（正整数）: "))
                if value > 0:
                    break
                print("请输入大于0的整数！")
            except ValueError:
                print("输入格式错误，请输入数字！")
        vote_mode = 'max' if mode == '最大' else 'fixed'
        break

    # 初始化候选人（保持不变）
    voting_system = VotingSystem(vote_mode, value)
    print("\n=== 候选人添加环节 ===")
    while True:
        name = input("请输入候选人姓名（输入空结束添加）: ").strip()
        if not name:
            if not voting_system.candidates:
                print("至少需要添加1位候选人！")
                continue
            break
        cid = voting_system.add_candidate(name)
        print(f"已添加候选人{cid}：{name}")

    # 启动服务器
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = ('192.168.1.105', 9999)
    try:
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(server_address)
        server_socket.listen(5)
        print(f'服务器启动成功，监听 {server_address[0]}:{server_address[1]}')
    except Exception as e:
        print(f"服务器启动失败：{str(e)}，请检查端口是否被占用")
        server_socket.close()
        return

    print("输入'保存'可随时保存结果，输入'退出'关闭服务器")
    key = os.urandom(16)
    iv = os.urandom(16)
    client_threads = []
    running = True  # 主循环控制标志

    # 输入处理子线程（关键优化点）
    def handle_input():
        nonlocal running
        while running:
            user_input = input("\n> ").strip().lower()
            if user_input == '保存':
                file_path = f"投票结果_{datetime.now().strftime('%Y%m%d%H%M%S')}.txt"
                print(voting_system.save_to_file(file_path))
            elif user_input == '退出':
                running = False
                print("正在快速关闭服务器...")
                # 关键优化：主动关闭server_socket中断accept()阻塞
                try:
                    server_socket.close()
                except Exception:
                    pass
            else:
                print("无效命令，输入'保存'或'退出'")

    input_thread = threading.Thread(target=handle_input, daemon=True)
    input_thread.start()

    # 主线程监听连接（关键优化点）
    try:
        while running:
            try:
                # 设置超时避免永久阻塞（优化点1）
                server_socket.settimeout(1)
                client_socket, client_address = server_socket.accept()
                print(f"新客户端连接：{client_address}")
                client_thread = threading.Thread(
                    target=handle_client,
                    args=(client_socket, voting_system, key, iv),
                    daemon=True  # 守护线程随主线程退出自动终止（优化点2）
                )
                client_thread.start()
                client_threads.append(client_thread)
            except socket.timeout:
                # 超时后检查running状态，避免阻塞（优化点3）
                if not running:
                    break
            except OSError as e:
                # server_socket被关闭时触发此异常（优化点4）
                if running:
                    print(f"接收连接异常: {str(e)}")
                break
    finally:
        # 确保清理所有资源
        running = False
        server_socket.close()
        # 等待守护线程自然结束（非阻塞）
        print("服务器已关闭所有连接")

    # 保存最终结果（保持不变）
    file_path = f"最终投票结果_{datetime.now().strftime('%Y%m%d%H%M%S')}.txt"
    print(voting_system.save_to_file(file_path))
    print("服务器完全退出")


if __name__ == "__main__":
    main()
